package com.maidada.codesharebackend.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.maidada.codesharebackend.mapper.CodeMapper;
import com.maidada.codesharebackend.model.entity.Code;
import com.maidada.codesharebackend.service.CodeViewService;
import com.maidada.codesharebackend.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 代码浏览记录 Service 实现类
 *
 * @author wulinxuan
 */
@Slf4j
@Service
public class CodeViewServiceImpl implements CodeViewService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CodeMapper codeMapper;

    /**
     * Redis key 前缀
     */
    private static final String PV_RECORDED_PREFIX = "pv:recorded:code:";

    /**
     * Redis key 过期时间（天）
     */
    private static final long EXPIRE_DAYS = 1;

    /**
     * 日期格式化器
     */
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 增加代码片段浏览次数（异步）
     * <p>
     * 逻辑：
     * 1. 根据用户登录状态和IP地址生成唯一的Redis key
     * 2. 检查今天是否已经记录过该用户/IP的浏览
     * 3. 如果未记录，则保存到Redis并更新MySQL中的浏览次数
     * 4. Redis key设置1天过期时间，避免重复统计
     *
     * @param codeId 代码片段ID
     * @param userId 用户ID（未登录时为null）
     * @param request HTTP请求对象
     */
    @Async("asyncTaskExecutor")
    @Override
    public void incrementViewCount(Long codeId, Long userId, HttpServletRequest request) {
        if (codeId == null || codeId <= 0) {
            log.warn("增加浏览次数失败：代码片段ID无效，codeId={}", codeId);
            return;
        }

        try {
            // 获取当前日期
            String currentDate = LocalDate.now().format(DATE_FORMATTER);

            // 生成Redis key
            String redisKey = buildRedisKey(codeId, userId, request, currentDate);

            // 检查今天是否已经记录过
            Boolean hasRecorded = stringRedisTemplate.hasKey(redisKey);
            if (hasRecorded) {
                // 今天已记录过浏览，跳过统计
                log.debug("今天已记录过浏览，跳过统计，key={}", redisKey);
                return;
            }

            // 保存到Redis，标记已记录
            Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                    redisKey,
                    "1",
                    EXPIRE_DAYS,
                    TimeUnit.DAYS
            );

            // 如果Redis保存成功，则更新MySQL中的浏览次数
            if (Boolean.TRUE.equals(success)) {
                updateViewCountInDatabase(codeId);
                log.info("浏览次数+1成功，codeId={}, key={}", codeId, redisKey);
            } else {
                log.debug("Redis保存失败，跳过统计，key={}", redisKey);
            }

        } catch (Exception e) {
            log.error("增加浏览次数失败，codeId={}, error={}", codeId, e.getMessage(), e);
        }
    }

    /**
     * 构建Redis key
     * <p>
     * 登录用户：pv:recorded:code:123:user:1001:date:20251030
     * 未登录用户：pv:recorded:code:123:ip:192_168_1_1:date:20251030
     *
     * @param codeId 代码片段ID
     * @param userId 用户ID（未登录时为null）
     * @param request HTTP请求对象
     * @param date 日期字符串
     * @return Redis key
     */
    private String buildRedisKey(Long codeId, Long userId, HttpServletRequest request, String date) {
        StringBuilder keyBuilder = new StringBuilder(PV_RECORDED_PREFIX);
        keyBuilder.append(codeId);

        // 判断用户是否登录
        if (userId != null) {
            // 登录用户：使用用户ID
            keyBuilder.append(":user:").append(userId);
        } else {
            // 未登录用户：使用IP地址
            String ip = IpUtil.getIpAddress(request);
            String formattedIp = IpUtil.formatIpForKey(ip);
            keyBuilder.append(":ip:").append(formattedIp);
        }

        // 添加日期
        keyBuilder.append(":date:").append(date);

        return keyBuilder.toString();
    }

    /**
     * 更新数据库中的浏览次数
     *
     * @param codeId 代码片段ID
     */
    private void updateViewCountInDatabase(Long codeId) {
        try {
            LambdaUpdateWrapper<Code> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Code::getId, codeId)
                    .setSql("view_count = view_count + 1");

            codeMapper.update(updateWrapper);
        } catch (Exception e) {
            log.error("更新数据库浏览次数失败，codeId={}, error={}", codeId, e.getMessage(), e);
        }
    }
}

